/**
 * @file
 *
 * Header del modulo per la gestione della griglia principale.
 *
 * La griglia principale viene gestita tramite la variabile ::grid di tipo
 * ::grid_s. Questa struttura contiene tutte le singole celle ed i relativi
 * status ed è il fulcro del programma.
 */

// Author: Adrian Sassatelli
// Copyright (c) 2014 Adrian Sassatelli


#include <gtk/gtk.h>


/* -------------------------------------------------------------------------- */
/*    DICHIARAZIONI                                                           */
/* -------------------------------------------------------------------------- */

/**
 * Enumeratore dello stato attuale di visualizzazione della casella.
 *
 * Sono stati scelti 4 stati diversi che rappresentano se la casella è allo
 * stato iniziale (::BS_NORMAL), se l'utente ha contrassegnato la casella come
 * contenente una mina (::BS_CHECKED), come probabilmente contenente una mina
 * (::BS_MAYBE) o come premuta (::BS_PRESSED) e quindi già visualizzato il suo
 * contenuto (guardare ::button_content_e).
 */
enum button_status_e {
    BS_NORMAL,      /**< Normale (stato iniziale, non premuto) */
    BS_CHECKED,     /**< Contenente una mina */
    BS_MAYBE,       /**< Probabilmente contenente una mina */
    BS_PRESSED      /**< Casella premuta */
};

/* -------------------------------------------------------------------------- */

/**
 * Enumeratore del contenuto della casella.
 *
 * Sono stati scelti 10 stati diversi che indicano la presenza o meno di una o
 * più mine nelle vicinanze della casella. La casella può contenere una mina
 * (::BC_MINE) o essere vuota ed avere o meno nelle caselle contigue una o più
 * mine (::BC_NEAR0..::BC_NEAR8).
 */
enum button_content_e {
    BC_NEAR0,   /**< Vuota con 0 mine vicino */
    BC_NEAR1,   /**< Vuota con 1 mine vicino */
    BC_NEAR2,   /**< Vuota con 2 mine vicino */
    BC_NEAR3,   /**< Vuota con 3 mine vicino */
    BC_NEAR4,   /**< Vuota con 4 mine vicino */
    BC_NEAR5,   /**< Vuota con 5 mine vicino */
    BC_NEAR6,   /**< Vuota con 6 mine vicino */
    BC_NEAR7,   /**< Vuota con 7 mine vicino */
    BC_NEAR8,   /**< Vuota con 8 mine vicino */
    BC_MINE     /**< Presenza della mina */
};

/* -------------------------------------------------------------------------- */

/**
 * Enumeratore dello stato della partita.
 *
 * Sono stati scelti 3 stati diversi che indicano lo stato attuale della
 * partita. Lo stato della partita può essere normale (::GS_NORMAL), partita
 * persa (::GS_LOST) o partita vinta (::GS_WIN).
 */
enum game_status_e {
    GS_NORMAL,    /**< Stato partita normale */
    GS_LOST,      /**< Stato partita persa */
    GS_WIN        /**< Stato partita vinta */
};

/* -------------------------------------------------------------------------- */

/**
 * Struttura dei dati relativi alla casella.
 *
 * Nella struttura si trovano due puntatori ad oggetto (::button_s::image e
 * ::button_s::container) utilizzati esclusivamente per la visualizzazione dello
 * stato e la gestione degli eventi della casella.
 *
 * Il widget ::button_s::image viene utilizzato per la visualizzazione grafica
 * della casella. \n
 * Il widget ::button_s::container viene utilizzato per la gestione degli eventi
 * ed incapsula il widget ::button_s::image.
 */
struct button_s {
    GtkImage *image;            /**< Puntatore al widget che gestisce la
                                 * visualizzazione dello stato della casella */
    GtkWidget *container;       /**< Puntatore al widget che gestisce gli
                                 * eventi della casella */

    button_status_e status;     /**< Stato della casella */
    button_content_e content;   /**< Contenuto della casella */

    int coln;                   /**< Colonna della casella all'interno della
                                 * griglia */
    int rown;                   /**< Riga della casella all'interno della
                                 * griglia */
};

/* -------------------------------------------------------------------------- */

/**
 * Struttura principale contenente tutte le caselle e lo stato corrente della
 * partita.
 */
struct grid_s {
    button_s *buttons;            /**< Caselle */

    int cols;                     /**< Numero di colonne della griglia */
    int rows;                     /**< Numero di righe della griglia */

    int nummines;                 /**< Numero di mine presenti */
    int selmines;                 /**< Numero di mine selezionate */

    game_status_e game_status;    /**< La partita è stata persa */
};


/* -------------------------------------------------------------------------- */
/*    BUTTON                                                                  */
/* -------------------------------------------------------------------------- */

/**
 * Cambia lo stato della singola casella.
 *
 * Nel caso in cui si passi come nuovo stato ::BS_PRESSED, viene fatto anche un
 * controllo del contenuto della casella e, se il contenuto è ::BC_MINE, la
 * partita si ferma.
 *
 * @param[in] acol Numero di colonna della casella
 * @param[in] arow Numero di riga della casella
 * @param[in] astatus Nuovo stato
 */
void button_change_status (const int acol, const int arow, const button_status_e astatus);

/* -------------------------------------------------------------------------- */

/**
 * Cambia stato alle caselle adiacenti a quella data.
 *
 * Analizza le caselle adiacenti cambiandone lo stato in ::BS_PRESSED nel caso
 * in cui siano comnprese tra ::BC_NEAR0 e ::BC_NEAR1. \n
 * Se una casella adiacente è ::BC_NEAR0, ne analizza le adiacenti tramite
 * ricorsione.
 *
 * @param[in] acol Numero di colonna della casella
 * @param[in] arow Numero di riga della casella
 */
void button_reveal (const int acol, const int arow);

/* -------------------------------------------------------------------------- */

/**
 * Esegui procedure per tasto sinistro su casella.
 *
 * Cambia lo stato della casella in ::BS_PRESSED, controlla il contenuto ed
 * eventualmente rivela le caselle adiacenti.
 *
 * @param[in] acol Numero di colonna della casella
 * @param[in] arow Numero di riga della casella
 */
void button_left_click (const int acol, const int arow);

/* -------------------------------------------------------------------------- */

/**
 * Esegui procedure per tasto destro su casella.
 *
 * Cambia lo stato della casella permettendo di identificarla come casella con
 * mina presente (::BS_CHECKED), probabilmente presente (::BS_MAYBE) o normale
 * (::BS_NORMAL).
 *
 * Il cambio di stato è ciclico: \n
 * ::BS_NORMAL -> ::BS_CHECKED -> ::BS_MAYBE -> ::BS_NORMAL -> ...
 *
 * @param[in] acol Numero di colonna della casella
 * @param[in] arow Numero di riga della casella
 */
void button_right_click (const int acol, const int arow);


/* -------------------------------------------------------------------------- */
/*    GRID                                                                    */
/* -------------------------------------------------------------------------- */

/**
 * Controlla se la partita è stata vinta.
 *
 * Il controllo avviene in due modi:
 * * il numero di caselle segnate è uguale al numero di mine ed ognuna contiene
 *   una mina
 * * il numero di caselle rimaste è uguale al numero mine (non c'è bisogno di
 *   controllare il contenuto)
 */
gboolean grid_check_win ();

/* -------------------------------------------------------------------------- */

/**
 * Cancella la griglia principale.
 *
 * La funzione azzera la griglia principale distruggendo le caselle ed
 * impostando i valori ad uno stato iniziale.
 */
void grid_delete ();

/* -------------------------------------------------------------------------- */

/**
 * Popola la griglia principale.
 *
 * La funzione esegue le seguenti procedure:
 * * azzera la griglia principale (nel caso che non sia già stata azzerata e
 *   nel caso in cui abbia dimensioni diverse da quella precedente)
 * * inizializza la griglia
 * * popola la griglia con le mine e con la quantità di mine presenti vicino
 *   alle caselle.
 *
 * @todo Per aggiungere le caselle alla griglia viene utilizzato
 * \b gtk_grid_attach che sembra il motivo principale della lentezza quando la
 * griglia comincia ad assumere dimensioni medio/grandi. \n
 * Si potrebbe cercare una procedura diversa tipo \b gtk_grid_attach_next_to o
 * \b gtk_box_pack_start. \n
 * Se l'aggiunta è ancora lenta, si potrebbe sostituire la coppia di widgets
 * GtkEventBox e GtkImage con il widget GtkButton.
 *
 * @param[in] acols Numero di colonne della griglia (MIN 1)
 * @param[in] arows Numero di righe della griglia (MIN 1)
 * @param[in] anummines Numero di mine (MAX arows * acols - 1)
 * @param[inout] agrid Griglia dove verranno agganciate le caselle
 * @param[in] ahandler Nome della funzione che gestirà l'evento click su ogni
 *            singola casella
 * @return True se la funzione è andata a buon fine, altrimenti false.
 */
gboolean grid_fill (const int acols, const int arows, const int anummines, GtkWidget *agrid, GCallback ahandler);

/* -------------------------------------------------------------------------- */

/**
 * Inizializza la griglia principale.
 *
 * Si utilizza unicamente dopo aver creato la variabile ::grid per impostare i
 * valori principali della struttura.
 */
void grid_init ();

/* -------------------------------------------------------------------------- */

/**
 * Partita persa, rivela le caselle delle mine.
 *
 * Rivela la posizione di ogni mina rimasta ed eventualmente di quelle segnate
 * in modo errato.
 */
void grid_lost_reveal ();

/* -------------------------------------------------------------------------- */

/**
 * Partita vinta, rivela le caselle delle mine.
 */
void grid_win_reveal ();

/* -------------------------------------------------------------------------- */
